package bx.job.jobstore.processor;

import bx.job.jobstore.MybatisPlusBootstrap;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;


public class SqlSessionFactoryProcessor {

    private static volatile SqlSessionFactory SQL_SESSION_FACTORY;

    private static final String MAPPER_XML_LOCATION = "mybatis-mapper";
    private static final String MAPPER_PACKAGE_NAME = "mapper";

    private static final String ENVIRONMENT_NAME = "bx-job-environment";


    public static SqlSession getSqlSession() {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        return sqlSessionFactory.openSession(false);
    }

    public static void putSqlSession(SqlSession sqlSession) {
        sqlSession.commit();
        sqlSession.close();
    }

    private static SqlSessionFactory getSqlSessionFactory() {
        if (SQL_SESSION_FACTORY == null) {
            synchronized (DataSourceProcessor.class) {
                if (SQL_SESSION_FACTORY == null) {
                    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
                    // mybatis-plus 配置对象，对 mybatis Configuration 增强
                    MybatisConfiguration configuration = new MybatisConfiguration();
                    // 初始化配置
                    initConfiguration(configuration);
                    // 初始化连接器，如 mybatis-plus的分页插件
                    configuration.addInterceptor(initInterceptor());
                    // 配置日志实现
                    configuration.setLogImpl(Slf4jImpl.class);
                    // 扫描 mapper 接口所在包
                    configuration.addMappers(MAPPER_PACKAGE_NAME);
                    // 构建 mybatis-plus需要的 GlobalConfig
                    GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
                    // 自动生成实现 baseMapper 基础方法映射
                    globalConfig.setSqlInjector(new DefaultSqlInjector());
                    // 设置id生成器
                    globalConfig.setIdentifierGenerator(new DefaultIdentifierGenerator());
                    // 设置父类 mapper
                    globalConfig.setSuperMapperClass(BaseMapper.class);
                    // 设置数据源
                    Environment environment = new Environment(ENVIRONMENT_NAME, new JdbcTransactionFactory(), DataSourceProcessor.getDataSource());
                    configuration.setEnvironment(environment);
                    registryMapperXml(configuration, MAPPER_XML_LOCATION);
                    SQL_SESSION_FACTORY = builder.build(configuration);
                }
            }
        }
        return SQL_SESSION_FACTORY;
    }

    private static void initConfiguration(MybatisConfiguration configuration) {
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setUseGeneratedKeys(true);
    }

    private static Interceptor initInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        paginationInnerInterceptor.setOverflow(true);
        paginationInnerInterceptor.setMaxLimit(500L);
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }

    private static void registryMapperXml(MybatisConfiguration configuration, String classPath) {
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            Enumeration<URL> mapper = contextClassLoader.getResources(classPath);

            while (mapper.hasMoreElements()) {
                URL url = mapper.nextElement();
                if (url.getProtocol().equals("file")) {
                    String path = url.getPath();
                    File file = new File(path);
                    File[] files = file.listFiles();
                    for (File f : files) {
                        FileInputStream in = new FileInputStream(f);
                        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(in, configuration, f.getPath(), configuration.getSqlFragments());
                        xmlMapperBuilder.parse();
                        in.close();
                    }
                } else {
                    JarURLConnection urlConnection = (JarURLConnection) url.openConnection();
                    JarFile jarFile = urlConnection.getJarFile();
                    Enumeration<JarEntry> entries = jarFile.entries();
                    while (entries.hasMoreElements()) {
                        JarEntry jarEntry = entries.nextElement();
                        if (jarEntry.getName().endsWith(".xml")) {
                            InputStream in = jarFile.getInputStream(jarEntry);
                            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(in, configuration, jarEntry.getName(), configuration.getSqlFragments());
                            xmlMapperBuilder.parse();
                            in.close();
                        }
                    }
                }
            }
        } catch (Exception e) {
            MybatisPlusBootstrap.getLogger().error("registryMapperXml fail", e);
        }
    }

}
